/*
 * Copyright 2011-2022 Branimir Karadzic. All rights reserved.
 * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
 */

#include <bx/uint32_t.h>
#include "common.h"
#include "bgfx_utils.h"
#include "bgfx_logo.h"
#include "imgui/imgui.h"
#include <iostream>
#include "camera/myCamera.h"

namespace
{

// use for shadow map; different to default framebuffer id
#define RENDER_SHADOW_PASS_ID 1
#define RENDER_SCENE_PASS_ID  2

	struct CubeVertex
	{
		float m_x;
		float m_y;
		float m_z;

		static void init()
		{
			ms_layout.begin().add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float).end();
		};
		static bgfx::VertexLayout ms_layout;
	};

	bgfx::VertexLayout CubeVertex::ms_layout;

	static CubeVertex s_cubeVertices[] =
	{
		{-0.5f,  0.5f,  0.5f },
		{ 0.5f,  0.5f,  0.5f },
		{-0.5f, -0.5f,  0.5f },
		{ 0.5f, -0.5f,  0.5f },
		{-0.5f,  0.5f, -0.5f },
		{ 0.5f,  0.5f, -0.5f },
		{-0.5f, -0.5f, -0.5f },
		{ 0.5f, -0.5f, -0.5f },
	};

	static const uint16_t s_cubeIndex[] =
	{
		0, 1, 2,
		1, 3, 2,
		4, 6, 5,
		5, 6, 7,
		0, 2, 4,
		4, 2, 6,
		1, 5, 3,
		5, 7, 3,
		0, 4, 1,
		4, 5, 1,
		2, 3, 6,
		6, 3, 7,
	};

	struct FloorVertex
	{
		float    m_x;
		float    m_y;
		float    m_z;
		uint32_t m_normal;

		static void init()
		{
			ms_layout
				.begin()
				.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float)
				.add(bgfx::Attrib::Normal, 4, bgfx::AttribType::Uint8, true, true)
				.end();
		};

		static bgfx::VertexLayout ms_layout;
	};

	bgfx::VertexLayout FloorVertex::ms_layout;

	static FloorVertex s_floorVertices[] =
	{
		{ -1.0f, 0.0f,  1.0f, encodeNormalRgba8(0.0f, 1.0f, 0.0f) },
		{  1.0f, 0.0f,  1.0f, encodeNormalRgba8(0.0f, 1.0f, 0.0f) },
		{ -1.0f, 0.0f, -1.0f, encodeNormalRgba8(0.0f, 1.0f, 0.0f) },
		{  1.0f, 0.0f, -1.0f, encodeNormalRgba8(0.0f, 1.0f, 0.0f) },
	};

	static const uint16_t s_floorIndices[] =
	{
		0, 1, 2,
		1, 3, 2,
	};

	static const  bx::Vec3 lightCubePosition = { 5.0f, 0.0f, -6.0f };

	class EStarHomework : public entry::AppI
	{
	public:
		EStarHomework(const char* _name, const char* _description, const char* _url)
			: entry::AppI(_name, _description, _url)
		{
			m_width = 0;
			m_height = 0;
			m_debug = BGFX_DEBUG_NONE;
			m_reset = BGFX_RESET_NONE;
		}

		void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
		{
			Args args(_argc, _argv);

			m_width = _width;
			m_height = _height;
			m_debug = BGFX_DEBUG_TEXT;
			m_reset = BGFX_RESET_VSYNC;

			bgfx::Init init;
			init.type = bgfx::RendererType::OpenGL;
			//init.type = bgfx::RendererType::Direct3D11;
			init.vendorId = args.m_pciId;
			init.resolution.width = m_width;
			init.resolution.height = m_height;
			init.resolution.reset = m_reset;
			bgfx::init(init);

			// start  time
			m_timeOffset = bx::getHPCounter();
			m_pretime = m_timeOffset;

			// set camera
			myCameraCreate();
			myCameraSetPosition({ 0.0f, 0.0f, -30.0f });
			myCameraSetVerticalAngle(0);
			m_fov = 60.f;

			// Enable debug text.
			bgfx::setDebug(m_debug);

			// Set view 0 clear state.
			bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f, 0);

			// set shader uniform
			s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
			s_texNormal = bgfx::createUniform("s_texNormal", bgfx::UniformType::Sampler);
			s_texAorm = bgfx::createUniform("s_texAorm", bgfx::UniformType::Sampler);
			s_texLod = bgfx::createUniform("s_texLod", bgfx::UniformType::Sampler);
			s_texIrr = bgfx::createUniform("s_texIrr", bgfx::UniformType::Sampler);
			s_texShadowmap = bgfx::createUniform("s_texShadowmap", bgfx::UniformType::Sampler);

			u_lightColor = bgfx::createUniform("u_lightColor", bgfx::UniformType::Vec4);
			u_lightPos = bgfx::createUniform("u_lightPos", bgfx::UniformType::Vec4);
			u_lightMtx = bgfx::createUniform("u_lightMtx", bgfx::UniformType::Mat4);
			u_viewPos = bgfx::createUniform("u_viewPos", bgfx::UniformType::Vec4);
			u_material = bgfx::createUniform("u_material", bgfx::UniformType::Vec4);
			u_environment = bgfx::createUniform("u_environment", bgfx::UniformType::Vec4);
			u_depthScaleOffset = bgfx::createUniform("u_depthScaleOffset", bgfx::UniformType::Vec4);
			u_controlBlinnphong = bgfx::createUniform("u_controlBlinnphong", bgfx::UniformType::Vec4);
			u_lightMode = bgfx::createUniform("u_lightMode", bgfx::UniformType::Vec4);

			// load model
			m_meshStone = meshLoad("resource/pbr_stone/tan_stone.bin");
			m_meshBunny = meshLoad("resource/basic_meshes/bunny.bin"); 
			m_meshColumn = meshLoad("resource/basic_meshes/column.bin");

			// load texture
			m_textureColor = loadTexture("resource/pbr_stone/pbr_stone_base_color.dds");
			m_textureNormal = loadTexture("resource/pbr_stone/pbr_stone_normal.dds");
			m_textureAorm = loadTexture("resource/pbr_stone/pbr_stone_aorm.dds");
			m_texLod = loadTexture("resource/env_maps/bolonga_lod.dds", BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP);
			m_texIrr = loadTexture("resource/env_maps/bolonga_irr.dds", BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP);

			// load shader program
			m_blinnPhongProgram = loadProgram("vs_blinnphong", "fs_blinnphong");
			m_pbrProgram = loadProgram("vs_pbr", "fs_pbr");
			m_lightCubeProgram = loadProgram("vs_lightCube", "fs_lightCube");
			m_skyboxProgram = loadProgram("vs_skybox", "fs_skybox");
			m_iblProgram = loadProgram("vs_ibl", "fs_ibl");
			m_pbriblProgram = loadProgram("vs_pbribl", "fs_pbribl");
			m_shadowmapProgram = loadProgram("vs_shadowmap", "fs_shadowmap");
			m_shadowmeshProgram = loadProgram("vs_shadowmesh", "fs_shadowmesh");

			// set light cube
			CubeVertex::init();
			m_cubeVbh = bgfx::createVertexBuffer(bgfx::makeRef(s_cubeVertices, sizeof(s_cubeVertices)), CubeVertex::ms_layout);
			m_cubeIbh = bgfx::createIndexBuffer(bgfx::makeRef(s_cubeIndex, sizeof(s_cubeIndex)));

			// set floor
			FloorVertex::init();
			m_floorVbh = bgfx::createVertexBuffer(bgfx::makeRef(s_floorVertices, sizeof(s_floorVertices)), FloorVertex::ms_layout);
			m_floorIbh = bgfx::createIndexBuffer(bgfx::makeRef(s_floorIndices, sizeof(s_floorIndices)));
			m_shadowmapSize = 512;

			// set shadow framebuffer
			m_shadowmapFB = BGFX_INVALID_HANDLE;

			// set shadow pass state
			
			bgfx::TextureHandle fbtextures[] =
			{
				bgfx::createTexture2D(m_shadowmapSize, m_shadowmapSize, false, 1, bgfx::TextureFormat::BGRA8, BGFX_TEXTURE_RT),
				bgfx::createTexture2D(m_shadowmapSize, m_shadowmapSize, false, 1, bgfx::TextureFormat::D16, BGFX_TEXTURE_RT_WRITE_ONLY),
			};
			m_texShadowmap = fbtextures[0];
			m_shadowmapFB = bgfx::createFrameBuffer(BX_COUNTOF(fbtextures), fbtextures, true);
			m_state[0] = meshStateCreate();
			m_state[0]->m_state = 0;
			m_state[0]->m_program = m_shadowmapProgram;
			m_state[0]->m_viewId = RENDER_SHADOW_PASS_ID;
			m_state[0]->m_numTextures = 0;
			m_state[0]->m_state = 0 
				| BGFX_STATE_WRITE_RGB 
				| BGFX_STATE_WRITE_A
				| BGFX_STATE_WRITE_Z 
				| BGFX_STATE_DEPTH_TEST_LESS 
				| BGFX_STATE_CULL_CCW 
				| BGFX_STATE_MSAA;
			m_state[1] = meshStateCreate();
			m_state[1]->m_state = 0
				| BGFX_STATE_WRITE_RGB
				| BGFX_STATE_WRITE_A
				| BGFX_STATE_WRITE_Z
				| BGFX_STATE_DEPTH_TEST_LESS
				| BGFX_STATE_CULL_CCW
				| BGFX_STATE_MSAA
				;
			m_state[1]->m_program = m_shadowmeshProgram;
			m_state[1]->m_viewId = RENDER_SCENE_PASS_ID;
			m_state[1]->m_numTextures = 0;

			//set init light mode
			m_lightMode = 4;

			// set material parameter
			m_roughness = 0.3f;
			m_metallic = 0.8f;

			imguiCreate();
		}

		virtual int shutdown() override
		{
			myCameraDestroy();

			imguiDestroy();
			meshStateDestroy(m_state[0]);
			meshStateDestroy(m_state[1]);

			bgfx::destroy(m_blinnPhongProgram);
			bgfx::destroy(m_pbrProgram);
			bgfx::destroy(m_iblProgram);
			bgfx::destroy(m_skyboxProgram);
			bgfx::destroy(m_pbriblProgram);
			bgfx::destroy(m_lightCubeProgram);
			bgfx::destroy(m_shadowmapProgram);
			bgfx::destroy(m_shadowmeshProgram);

			bgfx::destroy(m_textureColor);
			bgfx::destroy(m_textureNormal);
			bgfx::destroy(m_textureAorm);
			bgfx::destroy(m_texLod);
			bgfx::destroy(m_texIrr);

			bgfx::destroy(s_texColor);
			bgfx::destroy(s_texNormal);
			bgfx::destroy(s_texAorm);
			bgfx::destroy(s_texLod);
			bgfx::destroy(s_texIrr);
			bgfx::destroy(s_texShadowmap);

			bgfx::destroy(m_cubeVbh);
			bgfx::destroy(m_cubeIbh);
			bgfx::destroy(m_floorVbh);
			bgfx::destroy(m_floorIbh);

			bgfx::destroy(u_lightColor);
			bgfx::destroy(u_lightPos);
			bgfx::destroy(u_material);
			bgfx::destroy(u_environment);
			bgfx::destroy(u_controlBlinnphong);
			bgfx::destroy(u_lightMode);

			bgfx::destroy(m_shadowmapFB);

			meshUnload(m_meshBunny);
			meshUnload(m_meshColumn);
			meshUnload(m_meshStone);

			bgfx::shutdown();
			return 0;
		}

		bool update() override
		{
			if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState))
			{
				imguiBeginFrame(m_mouseState.m_mx
					, m_mouseState.m_my
					, (m_mouseState.m_buttons[entry::MouseButton::Left] ? IMGUI_MBUT_LEFT : 0)
					| (m_mouseState.m_buttons[entry::MouseButton::Right] ? IMGUI_MBUT_RIGHT : 0)
					| (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
					, m_mouseState.m_mz
					, uint16_t(m_width)
					, uint16_t(m_height)
				);

				showExampleDialog(this);
				ImGui::SetNextWindowPos(ImVec2(m_width - m_width / 5.0f - 150.0f, 10.0f), ImGuiCond_FirstUseEver);
				ImGui::SetNextWindowSize(ImVec2(m_width / 5.0f, m_height / 16.0f), ImGuiCond_FirstUseEver);
				ImGui::Text("Camera:");
				if (ImGui::Button("Reset")) {
					myCameraReset();
				};
				ImGui::Separator();
				ImGui::Text("Light Mode:");
				ImGui::RadioButton("Blinn Phong", &m_lightMode, 0);
				ImGui::RadioButton("Pbr", &m_lightMode, 1);
				ImGui::RadioButton("Ibl", &m_lightMode, 2);
				ImGui::RadioButton("Pbr + Ibl", &m_lightMode, 3);
				ImGui::RadioButton("Pbr + shadow map", &m_lightMode, 4);
				ImGui::Separator();

				float nowTime = (float)(bx::getHPCounter());
				double frequency = bx::getHPFrequency();
				float deltaTime = (float)((nowTime - m_pretime) / frequency);
				m_pretime = nowTime;

				// set view 0 default viewport.
				bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height));

				myCameraUpdate(deltaTime*0.35f, m_mouseState, ImGui::MouseOverArea());

				bgfx::touch(0);

				myCameraGetViewMtx(m_viewMtx);
				m_fov = myCameraFOV();
				bx::mtxProj(m_projMtx, m_fov, float(m_width) / float(m_height), 0.1f, 100.0f, bgfx::getCaps()->homogeneousDepth);
				bgfx::setViewTransform(0, m_viewMtx, m_projMtx);

				if (m_lightMode == 0)
				{
					ImGui::Text(" \n use blinn phong", m_lightMode);

					static bool doBlinnPhongSpecular, doBlinnPhongDiffuse, doBlinnPhongAmbient;
					ImGui::Checkbox("diffuse", &doBlinnPhongDiffuse);
					ImGui::Checkbox("specular", &doBlinnPhongSpecular);
					ImGui::Checkbox("ambient", &doBlinnPhongAmbient);

					makeLightCube();

					// draw mesh
					// set meshModelMtx
					bx::mtxTranslate(m_meshStoneModelMtx, -5.0f, -5.0f, 0.0f);
					// set uniform  
					float tmpLightColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
					float tmpViewPos[4] = { myCameraGetPosition().x, myCameraGetPosition().y, myCameraGetPosition().z, 1.0f };
					float tmpLightPos[4] = { lightCubePosition.x, lightCubePosition.y, lightCubePosition.z, 1.0f };
					float tmpControlBlinnphong[4] = { doBlinnPhongDiffuse, doBlinnPhongSpecular, doBlinnPhongAmbient, 1.0f}; //x:doBlinnPhongDiffuse y:doBlinnPhongSpecular z:doBlinnPhongAmbient
					bgfx::setUniform(u_controlBlinnphong, &tmpControlBlinnphong);
					bgfx::setUniform(u_lightColor, &tmpLightColor);
					bgfx::setUniform(u_lightPos, &tmpLightPos);
					bgfx::setUniform(u_viewPos, &tmpViewPos);
					bgfx::setTexture(0, s_texColor, m_textureColor);
					bgfx::setTexture(1, s_texNormal, m_textureNormal);
					meshSubmit(m_meshStone, 0, m_blinnPhongProgram, m_meshStoneModelMtx);
				}
				else if (m_lightMode == 1)
				{
					ImGui::Text("\n use pbr", m_lightMode);

					static int controlRonghnessMetallic = 0;
					static bool showNormal = false;
					ImGui::Checkbox("showNormal", &showNormal);
					ImGui::Text("\n roughness and metallic", m_lightMode);
					ImGui::RadioButton("control by slider", &controlRonghnessMetallic, 0); ImGui::SameLine();
					ImGui::RadioButton("control by texture", &controlRonghnessMetallic, 1);
					if (controlRonghnessMetallic == 0) {
						ImGui::SliderFloat("roughness", &m_roughness, 0.01f, 1.0f);
						ImGui::SliderFloat("metallic", &m_metallic, 0.01f, 1.0f);
					}
					else if (controlRonghnessMetallic == 1) {
						float tmpMaterial[4] = { m_roughness, m_metallic, 1.0f, 0.0f };  //x:roughnes  y:metallic  z:controlRonghnessMetallic  w:shownormal
					}
					float tmpMaterial[4] = { m_roughness, m_metallic, float(controlRonghnessMetallic), showNormal };  //x:roughnes  y:metallic z:controlRonghnessMetallic
					bgfx::setUniform(u_material, &tmpMaterial);

					makeLightCube();

					// draw mesh
					// set meshModelMtx
					bx::mtxTranslate(m_meshStoneModelMtx, -5.0f, -5.0f, 0.0f);
					// set uniform  
					float tmpLightColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
					float tmpViewPos[4] = { myCameraGetPosition().x, myCameraGetPosition().y, myCameraGetPosition().z, 1.0f };
					float tmpLightPos[4] = { lightCubePosition.x, lightCubePosition.y, lightCubePosition.z, 1.0f };
					bgfx::setUniform(u_lightColor, &tmpLightColor);
					bgfx::setUniform(u_lightPos, &tmpLightPos);
					bgfx::setUniform(u_viewPos, &tmpViewPos);

					bgfx::setTexture(0, s_texColor, m_textureColor);
					bgfx::setTexture(1, s_texNormal, m_textureNormal);
					bgfx::setTexture(2, s_texAorm, m_textureAorm);
					meshSubmit(m_meshStone, 0, m_pbrProgram, m_meshStoneModelMtx);
				}
				else if (m_lightMode == 2) {					
					ImGui::Text("\n use ibl (only ambient)", m_lightMode);

					static bool doEnvSpecular, doEnvDiffuse;
					static float mipLevel;
					
					ImGui::Checkbox("environment diffuse", &doEnvDiffuse);
					ImGui::Checkbox("environment specular", &doEnvSpecular);
					if (doEnvSpecular) {
						ImGui::SliderFloat("mip level", &mipLevel, 1.0f, 6.0f);
					}

					makeSkybox();

					// draw mesh
					// set meshModelMtx
					float tmpRotate[16];
					float tmpScale[16];
					bx::mtxRotateY(tmpRotate, 3.1415926f);
					bx::mtxScale(tmpScale, 7.0f);
					bx::mtxMul(m_meshBunnyModelMtx,tmpScale, tmpRotate);
					m_meshBunnyModelMtx[13] = -7.0f;
					bgfx::setTransform(m_meshBunnyModelMtx);			
					// set uniform  
					float tmpLightColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
					float tmpEnvironment[4] = { doEnvSpecular, doEnvDiffuse, mipLevel, 0.0f };
					bgfx::setUniform(u_lightColor, &tmpLightColor);
					bgfx::setUniform(u_environment, &tmpEnvironment);
					bgfx::setTexture(3, s_texLod, m_texLod);
					bgfx::setTexture(4, s_texIrr, m_texIrr);
					meshSubmit(m_meshBunny, 0, m_iblProgram, m_meshBunnyModelMtx);
				}
				else if (m_lightMode == 3) {
					ImGui::Text("\n use pbr + ibl", m_lightMode);
					static int controlRonghnessMetallic = 0;
					ImGui::Text("\n roughness and metallic", m_lightMode);
					ImGui::RadioButton("control by slider", &controlRonghnessMetallic, 0); ImGui::SameLine();
					ImGui::RadioButton("control by texture", &controlRonghnessMetallic, 1);
					if (controlRonghnessMetallic == 0) {
						ImGui::SliderFloat("roughness", &m_roughness, 0.01f, 1.0f);
						ImGui::SliderFloat("metallic", &m_metallic, 0.01f, 1.0f);
					}
					else if (controlRonghnessMetallic == 1) {
						float tmpMaterial[4] = { m_roughness, m_metallic, 1.0f, 1.0f };  //x:roughnes  y:metallic  z:controlRonghnessMetallic
					}
					float tmpMaterial[4] = { m_roughness, m_metallic, float(controlRonghnessMetallic), 1.0f };  //x:roughnes  y:metallic z:controlRonghnessMetallic
					bgfx::setUniform(u_material, &tmpMaterial);

					makeLightCube();

					makeSkybox();

					// draw mesh
					bx::mtxTranslate(m_meshStoneModelMtx, -5.0f, -5.0f, 0.0f);
					// set uniform  
					float tmpLightColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
					float tmpViewPos[4] = { myCameraGetPosition().x, myCameraGetPosition().y, myCameraGetPosition().z, 1.0f };
					float tmpLightPos[4] = { lightCubePosition.x, lightCubePosition.y, lightCubePosition.z, 1.0f };
					bgfx::setUniform(u_lightColor, &tmpLightColor);
					bgfx::setUniform(u_lightPos, &tmpLightPos);
					bgfx::setUniform(u_viewPos, &tmpViewPos);

					bgfx::setTexture(0, s_texColor, m_textureColor);
					bgfx::setTexture(1, s_texNormal, m_textureNormal);
					bgfx::setTexture(2, s_texAorm, m_textureAorm);
					bgfx::setTexture(3, s_texLod, m_texLod);
					bgfx::setTexture(4, s_texIrr, m_texIrr);
					meshSubmit(m_meshStone, 0, m_pbriblProgram, m_meshStoneModelMtx);				

				}
				else if (m_lightMode == 4) {
					ImGui::Text("\n use pbr + shadow map", m_lightMode);

					// platform unified processing
					const bgfx::Caps* caps = bgfx::getCaps();
					float depthScaleOffset[4] = { 1.0f, 0.0f, 0.0f, 0.0f };
					if (caps->homogeneousDepth)
					{
						depthScaleOffset[0] = 0.5f;
						depthScaleOffset[1] = 0.5f;
					}
					bgfx::setUniform(u_depthScaleOffset, depthScaleOffset);

					// set light view matrix
					float tmpLightPos[4] = { lightCubePosition.x, lightCubePosition.y, lightCubePosition.z, 1.0f };
					bgfx::setUniform(u_lightPos, tmpLightPos);
					float lightView[16];
					float lightProj[16];
					const bx::Vec3 lightAt = { 0.0f,  -5.0f,   0.0f };
					const bx::Vec3 lightEye = { tmpLightPos[0], tmpLightPos[1], tmpLightPos[2] };
					bx::mtxLookAt(lightView, lightEye, lightAt);
					const float area = 30.0f;
					bx::mtxOrtho(lightProj, -area, area, -area, area, -100.0f, 100.0f, 0.0f, caps->homogeneousDepth);
					bgfx::setViewRect(RENDER_SHADOW_PASS_ID, 0, 0, m_shadowmapSize, m_shadowmapSize);
					bgfx::setViewFrameBuffer(RENDER_SHADOW_PASS_ID, m_shadowmapFB);
					bgfx::setViewTransform(RENDER_SHADOW_PASS_ID, lightView, lightProj);
					bgfx::setViewRect(RENDER_SCENE_PASS_ID, 0, 0, uint16_t(m_width), uint16_t(m_height));
					bgfx::setViewTransform(RENDER_SCENE_PASS_ID, m_viewMtx, m_projMtx);
					bgfx::setViewClear(RENDER_SHADOW_PASS_ID, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f, 0);
					bgfx::setViewClear(RENDER_SCENE_PASS_ID, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f, 0);
				
					const float sy = caps->originBottomLeft ? 0.5f : -0.5f;
					const float sz = caps->homogeneousDepth ? 0.5f : 1.0f;
					const float tz = caps->homogeneousDepth ? 0.5f : 0.0f;

					// platform unified processing
					const float mtxCrop[16] =
					{
						0.5f, 0.0f, 0.0f, 0.0f,
						0.0f,   sy, 0.0f, 0.0f,
						0.0f, 0.0f, sz,   0.0f,
						0.5f, 0.5f, tz,   1.0f,
					};

					float mtxShadow[16];
					float lightMtx[16];
					float mtxTmp[16];
					bx::mtxMul(mtxTmp, lightProj, mtxCrop);
					bx::mtxMul(mtxShadow, lightView, mtxTmp);

					// draw floor
					bx::mtxSRT(m_floorModelMtx
						, 30.0f, 30.0f, 30.0f
						, 0.0f, 0.0f, 0.0f
						, 0.0, -10.0f, 0.0f
					);
					bx::mtxMul(lightMtx, m_floorModelMtx, mtxShadow);
					uint32_t cached = bgfx::setTransform(m_floorModelMtx);
					for (uint32_t pass = 0; pass < 2; ++pass)
					{
						const MeshState& st = *m_state[pass];
						bgfx::setTransform(cached);
						if (pass == 1)
						{	
							float tmpLightMode[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; // x = 0.0f:blinnphong   x = 1.0:pbr
							bgfx::setUniform(u_lightMode, tmpLightMode);
							bgfx::setTexture(4, s_texShadowmap, m_texShadowmap);
						}
						bgfx::setUniform(u_lightMtx, lightMtx);
						bgfx::setIndexBuffer(m_floorIbh);
						bgfx::setVertexBuffer(0, m_floorVbh);
						bgfx::setState(st.m_state);
						bgfx::submit(st.m_viewId, st.m_program);
					}

					// draw stone
					bx::mtxSRT(m_meshStoneModelMtx
						, 1.0f, 1.0f, 1.0f
						, 0.0f, 0.0f, 0.0f
						, 0.0f, -10.0f, 0.0f
					);
					bx::mtxMul(lightMtx, m_meshStoneModelMtx, mtxShadow);
					bgfx::setUniform(u_lightMtx, lightMtx);
					meshSubmit(m_meshStone, &m_state[0], 1, m_meshStoneModelMtx);
					
					// set uniform  
					float tmpLightMode[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; // x = 0.0f:blinnphong   x = 1.0f:pbr
					bgfx::setUniform(u_lightMode, tmpLightMode);
					bgfx::setUniform(u_lightMtx, lightMtx);
					float tmpLightColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
					float tmpViewPos[4] = { myCameraGetPosition().x, myCameraGetPosition().y, myCameraGetPosition().z, 1.0f };
					bgfx::setUniform(u_lightColor, &tmpLightColor);
					bgfx::setUniform(u_viewPos, &tmpViewPos);
					bgfx::setTexture(0, s_texColor, m_textureColor);
					bgfx::setTexture(1, s_texNormal, m_textureNormal);
					bgfx::setTexture(2, s_texAorm, m_textureAorm);
					bgfx::setTexture(4, s_texShadowmap, m_texShadowmap);
					meshSubmit(m_meshStone, &m_state[1], 1, m_meshStoneModelMtx);
				}

				bgfx::frame();

				imguiEndFrame();

				return true;
			}

			return false;
		}

		void makeSkybox() {
			
			// set sky box
			bx::mtxIdentity(m_skyboxModelMtx);
			bgfx::setTransform(m_skyboxModelMtx);
			bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
			bgfx::setVertexBuffer(0, m_cubeVbh);
			bgfx::setIndexBuffer(m_cubeIbh);
			bgfx::setTexture(3, s_texLod, m_texLod);
			bgfx::submit(0, m_skyboxProgram);
			bgfx::setViewTransform(0, m_viewMtx, m_projMtx);
		}

		void makeLightCube() {
			// draw light cube
			bx::mtxTranslate(m_lightCubeModelMtx, lightCubePosition.x, lightCubePosition.y, lightCubePosition.z);
			bgfx::setTransform(m_lightCubeModelMtx);
			//bgfx::setState(0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_MSAA);  //uncommand will make light cube invisible
			bgfx::setVertexBuffer(0, m_cubeVbh);
			bgfx::setIndexBuffer(m_cubeIbh);
			bgfx::submit(0, m_lightCubeProgram);
		}

		entry::MouseState m_mouseState;

		uint32_t m_width;
		uint32_t m_height;
		uint32_t m_debug;
		uint32_t m_reset;

		int64_t m_timeOffset;
		int64_t m_pretime;

		bgfx::UniformHandle s_texColor;
		bgfx::UniformHandle s_texNormal;
		bgfx::UniformHandle s_texAorm;
		bgfx::UniformHandle s_texLod;
		bgfx::UniformHandle s_texIrr;
		bgfx::UniformHandle s_texShadowmap;

		bgfx::UniformHandle u_lightPos;
		bgfx::UniformHandle u_lightMtx;
		bgfx::UniformHandle u_lightColor;
		bgfx::UniformHandle u_viewPos;
		bgfx::UniformHandle u_material;
		bgfx::UniformHandle u_environment;
		bgfx::UniformHandle u_depthScaleOffset;
		bgfx::UniformHandle u_controlBlinnphong;
		bgfx::UniformHandle u_lightMode;

		bgfx::TextureHandle m_textureColor;
		bgfx::TextureHandle m_textureNormal;
		bgfx::TextureHandle m_textureAorm;
		bgfx::TextureHandle m_texLod;
		bgfx::TextureHandle m_texIrr;
		bgfx::TextureHandle m_texShadowmap;

		float m_viewMtx[16];
		float m_projMtx[16];
		float m_fov;

		// mesh member
		Mesh* m_meshStone;
		float m_meshStoneModelMtx[16];
		Mesh* m_meshBunny;
		float m_meshBunnyModelMtx[16];
		Mesh* m_meshColumn;
		float m_meshColumnModelMtx[16];
		MeshState* m_state[2];

		// shader program handler
		bgfx::ProgramHandle m_blinnPhongProgram;
		bgfx::ProgramHandle m_pbrProgram;
		bgfx::ProgramHandle m_lightCubeProgram;
		bgfx::ProgramHandle m_skyboxProgram;
		bgfx::ProgramHandle m_iblProgram;
		bgfx::ProgramHandle m_pbriblProgram;
		bgfx::ProgramHandle m_shadowmapProgram;
		bgfx::ProgramHandle m_shadowmeshProgram;

		// shadowmap member
		bgfx::FrameBufferHandle m_shadowmapFB;
		uint16_t m_shadowmapSize;

		// cube member
		bgfx::VertexBufferHandle m_cubeVbh;
		bgfx::IndexBufferHandle m_cubeIbh;
		float m_lightCubeModelMtx[16];
		float m_skyboxModelMtx[16];

		// floor member
		bgfx::VertexBufferHandle m_floorVbh;
		bgfx::IndexBufferHandle m_floorIbh;
		float m_floorModelMtx[16];

		// light mode 0:blinnPhong;1:pbr;2:ibl;3:pbr+ibl;4:shadowmap
		int m_lightMode;

		// material parameter
		float m_roughness;
		float m_metallic;
	};

}

int _main_(int _argc, char** _argv)
{
	EStarHomework app("e-star-homework", "", "");
	return entry::runApp(&app, _argc, _argv);
}


